const utils = {
    /**
     * 获取元素的最终样式值
     * @param {DOMObject} ele  要获取属性的DOM对象
     * @param {string}    attr 要获取的属性名
     * @return {string}   attr样式的值
     */
    // ES6的对象方法的增强版写法 say () {}
    getStyle (ele, attr) {
        if (ele.currentStyle) {
            // IE
            return ele.currentStyle[attr]
        } else {
            return getComputedStyle(ele, false)[attr]
        }
    },
    /**
     * 添加事件监听
     * @param {DOMObject} ele         要添加监听的DOM元素
     * @param {string}    type        事件名称（不带on）
     * @param {function}  fn          事件处理函数
     * @param {boolean}   [isCapture] 可选参数，是否捕获，默认为false（冒泡），true代表捕获阶段
     */
    on (ele, type, fn, isCapture) {
        // 默认值false
        if (isCapture === undefined) isCapture = false
        if (window.attachEvent) {
            ele.attachEvent('on' + type, fn)
        } else {
            ele.addEventListener(type, fn, isCapture)
        }
    },
    /**
     * 移除事件监听
     * @param {DOMObject} ele         要添加监听的DOM元素
     * @param {string}    type        事件名称（不带on）
     * @param {function}  fn          事件处理函数
     * @param {boolean}   [isCapture] 可选参数，是否捕获，默认为false（冒泡），true代表捕获阶段
     */
    off (ele, type, fn, isCapture) {
        if (isCapture === undefined) isCapture = false
        if (window.detachEvent) {
            ele.detachEvent('on' + type, fn)
        } else {
            ele.removeEventListener(type, fn, isCapture)
        }
    },

    /**
     * 实现元素的匀速运动
     * @param {DOMObject} ele 运动的DOM元素
     * @param {string} attr 运动的属性名
     * @param {number} end 运动的终点值，单位px
     * @param {number} duration 运动的总时间，单位ms
     * @param {function} [fn]  运动结束之后的回调函数
     */
    move (ele, attr, end, duration, fn) {
        // 获取起点值
        var start = parseInt(this.getStyle(ele, attr))
        // 计算运动的总距离
        var distance = end - start
        // 计算总步数
        var steps = parseInt(duration / 20)
        // 计算速度：px/step
        var speed = distance / steps
        // 开始运动
        clearInterval(ele.timer) // 开新的定时器之前把上一次的先停止掉
        var n = 0 // 记录当前步数
        // 把定时器的timer写在ele对象的自定义属性上，确保唯一性
        ele.timer = setInterval(function () {
            // 往前走一步
            n++
            ele.style[attr] = start + n * speed + 'px'
            // 判断终点
            if (n === steps) {
                // 步数走完了，到终点了
                clearInterval(ele.timer)
                // 因为js的小数运算可能并不精确，所以在这里手动的拉到终点
                ele.style[attr] = end + 'px'
                // 调用fn，利用逻辑短路，fn有效才调用
                fn && fn()
            }
        }, 20)
    },

    /**
     * 实现元素的缓冲运动
     * @param {DOMObject} ele 运动的DOM元素
     * @param {string} attr 运动的属性名
     * @param {number} end 运动的终点值，单位px
     * @param {function} [fn]  运动结束之后的回调函数
     */
    move1 (ele, attr, end, fn) {
        end = parseInt(end) // 不是很有必要，但是保险起见取整
        // 获取当前位置
        var start = parseInt(this.getStyle(ele, attr))
        // 开启定时器之前先把上一次的清除
        clearInterval(ele.timer)
        ele.timer = setInterval(function () {
            // 每一步都要计算剩下距离
            var distance = end - start
            // 计算速度
            // 速度如果是正向运动向上取整，负向的就向下取整
            var speed = distance > 0 ? Math.ceil(distance / 10) : Math.floor(distance / 10)
            // 当前位置往前走，加上一个speed
            start += speed
            ele.style[attr] = start + 'px'
            // 判断终点
            if (start === end) {
                clearInterval(ele.timer)
                fn && fn()
            }
        }, 20)
    },

    /**
     * 获取的是元素到body的坐标
     * @param {DOMObject} ele 获取坐标的元素
     * @return {object} 得到的坐标值 { left, top }
     */
    getBodyPos (ele) {
        var left = 0, top = 0
        while (ele.offsetParent) {
            // 如果有边框边框也要加上
            left += ele.offsetLeft + ele.clientLeft
            top += ele.offsetTop + ele.clientTop
            // 把ele变成他的父级，为下一次计算做准备
            ele = ele.offsetParent
        }
        return { left, top }
    },

    /**
     * 存cookie
     * @param {string} key        要存的cookie的名称
     * @param {string} value      要存的cookie的值
     * @param {object} [options]  { path, expires } 例如： { path: '/', expires: 7} 存根路径7天过期，path的默认值就是'/'
     */
    setCookie (key, value, options) {
        // 如果options传了，那就是本身，没有传的话就给一个默认path: '/'
        // options = options || { path: '/' }
        // if (!options.path) {
        //     options.path = '/'
        // }

        // 当options传递了的话，可以使用对象合并，如果options里面有path字段，那么自己的path值会把前面的/ 覆盖
        //   如果options里没有path，就默认的/合并上去
        // 当options没有传，options就是默认的path: /
        options = options ? Object.assign({ path: '/' },  options) : { path: '/' }

        var str = `${key}=${encodeURIComponent(value)};path=${options.path}`
        if (options.expires) {
            var d = new Date()
            d.setDate(d.getDate() + options.expires)
            str += `;expires=${d.toUTCString()}`
        }
        document.cookie = str
    },

    /**
     * 取cookie
     * @param  {string} key 要取的cookie的名称
     * @return {string}     返回cookie的值
     */
    getCookie (key) {
        var str = document.cookie
        var cookies = str.split('; ')
        var obj = {}
        cookies.forEach(item => {
            var arr = item.split('=')
            obj[arr[0]] = decodeURIComponent(arr[1])
        })
        return obj[key]
    },

    /**
     * 发送ajax get请求
     * @param {string}   url      请求地址
     * @param {object}   query    请求携带的数据
     * @param {function} fn       请求成功之后的回调函数
     * @param {boolean}  [isJson] 是否是json格式，默认为true
     */
    get (url, query, fn, isJson = true) {
        // 如果有query，拼接query
        if (query) {
            url += '?'
            for (var key in query) {
                url += `${key}=${query[key]}&`
            }
            // 最后一个属性会多出一个&
            url = url.slice(0, -1)
        }

        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.send()
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    var data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText
                    fn && fn(data)
                }
            }
        }
    },

    /**
     * 发送ajax post请求
     * @param {string}   url      请求地址
     * @param {object}   query    请求携带的数据
     * @param {function} fn       请求成功之后的回调函数
     * @param {boolean}  [isJson] 是否是json格式，默认为true
     */
    post (url, query, fn, isJson = true) {
        var str = ''
        if (query) {
            for (var key in query) {
                str += `${key}=${query[key]}&`
            }
            str = str.slice(0, -1)
        }
        
        var xhr = new XMLHttpRequest()
        xhr.open('POST', url)
        // 在send之前设置请求头的contentType为urlencoded，告诉后端的数据传输的编码方式
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
        xhr.send(str)
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    var data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText
                    fn && fn(data)
                }
            }
        }
    },

    /**
     * jsonp请求
     * @param {string} url      地址
     * @param {string} cbName   回调函数名
     * @param {object} [query]  其他参数
     */
    jsonp (url, cbName, query) {
        // 动态创建script标签
        url += `?cb=${cbName}`
        if (query) {
            for (var key in query) {
                url += `&${key}=${query[key]}`
            }
        }
        var script = document.createElement('script')
        script.src = url
        // 只要script标签存在异步请求就发出去了
        document.body.appendChild(script)
        document.body.removeChild(script) // 过河拆桥
    },

    /**
     * 发送基于promise的ajax get请求
     * @param {string}   url      请求地址
     * @param {object}   query    请求携带的数据
     * @param {boolean}  [isJson] 是否是json格式，默认为true
     */
    fetch (url, query, isJson = true) {
        // 如果有query，拼接query
        if (query) {
            url += '?'
            for (var key in query) {
                url += `${key}=${query[key]}&`
            }
            // 最后一个属性会多出一个&
            url = url.slice(0, -1)
        }
        // 需要把promise return出去，这样调用这个方法的时候可以直接在后面.then
        return new Promise((resolve, reject) => {
            var xhr = new XMLHttpRequest()
            xhr.open('GET', url)
            xhr.send()
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        var data = isJson ? JSON.parse(xhr.responseText) : xhr.responseText
                        resolve(data)
                    } else {
                        reject()
                    }
                }
            }
        })
    }
}